(import
 (except (rnrs base) let-values map error)
 (only (guile)
       lambda* λ
       current-output-port)
 (srfi srfi-1))


(define (mappend fn the-list)
  (apply append (map fn the-list)))


;; ============
;; Exercise 1.1
;; ============
;; Define a version of last-name that handles "Rex Morgan MD", "Morton Downey, Jr.," etc.

(define (first-name name titles)
  ;; assume that there is at least one first name
  (cond
   [(null? name) '()]
   [(member (car name) titles)
    (first-name (cdr name) titles)]
   [else (car name)]))


(define (remove-titles name titles)
  ;; not using let, because it has not been introduced yet
  (cond
   [(null? name) '()]
   [(member (car name) titles)
    (remove-titles (cdr name) titles)]
   [else
    (cons (car name)
          (remove-titles (cdr name) titles))]))


(define (last-name name titles)
  "idea: first remove all titles, then remove the first name and what remains shall be the last name"
  (last (remove-titles name titles)))

(last-name '(Madam Major General Paula Jones)
           '(Mr Mrs Miss Ms Sir Madam Dr Admiral Major General Jr MD))

;; Notes:
;; For more just add to the list of titles.
;; However if there are names which are also titles, this system breaks badly.

;; ============
;; Exercise 1.2
;; ============
;; Write a function to exponentiate, or raise a number to an integer power.
;; For example: (power 3 2) = 3^2 = 9.
(define (exponentiate acc base exponent)
  (cond [(= exponent 0) acc]
        [else (exponentiate (* acc base)
                            base
                            (- exponent 1))]))

;; ============
;; Exercise 1.3
;; ============
;; Write an function that counts the number of atoms in an expression.
;: For example: (count-atoms '(a (b) c)) = 3.
;; Note that there is something of an ambiguity in this:
;; Should (a nil c) count as 3 atoms, or as 2, because ot is equivalent to (a () c)?
(define (atom? sth)
  (and (not (pair? sth))
       (not (null? sth))))

(define (count-atoms expr)
  (cond [(null? expr) 0]  ; or return 1 here for counting ()
        [(atom? expr) 1]
        [else (+ (count-atoms (car expr))
                 (count-atoms (cdr expr)))]))

;; ============
;; Exercise 1.4
;; ============
;; Write a function that counts the times an expression occurs anywhere within another expression.
;; For example: (count-anywhere 'a '(a ((a) b) a)) => 3
(define (count-anywhere seek-expr sth)
  (cond [(null? sth) 0]
        [(atom? sth)
         (if (eq? seek-expr sth) 1 0)]
        [(eq? seek-expr (car sth))
         (+ 1 (count-anywhere seek-expr (cdr sth)))]
        [else (+ (count-anywhere seek-expr (car sth))
                 (count-anywhere seek-expr (cdr sth)))]))

;; ============
;; Exercise 1.5
;; ============
;; Write a function to computer the dot product of 2 sequences of numbers, represented as lists.
;; The dot product is computed by multiplying the corresponding elements and then adding up the resulting products.
;; For example: (dot-product '(10 20) '(3 4)) = 10 * 3 + 20 * 4 = 110
(define (dot-product l1 l2)
  (apply + (map * l1 l2)))
